﻿using soma.Models;
using System;
using System.Collections.Generic;
using System.Configuration;
using System.IO;
using System.Linq;
using System.Net;
using System.Net.Mail;
using System.Web;
using System.Xml.Linq;

namespace soma.Services
{
    ///<summary>
    /// Class used to verify and maintain the list of registered API key.
    /// In order to prevent ilicit request, each email address may be associated
    /// with a maximum number of 5 API keys. The information is kept in an xml file
    /// from which we read and write the data
    ///</summary>
    public class APIKeyVerifier
    {
        #region Private Const Fields

        private const int MAXIMUM_NUMBER_HITS_PER_KEY = 1000;
        private string pathKeys;

        #endregion

        #region Private Static Fields

        private static APIKeyVerifier instance = null;
        private static Object locker = new Object();

        #endregion

        #region Private Fields

        private Dictionary<string, User> usersMappedAPIKeys = null;

        #endregion

        private APIKeyVerifier()
        {
            usersMappedAPIKeys = new Dictionary<string, User>();
            //   usersMappedAPIKeys.Add("demoKey", new User("teo.ptr@gmail.com", "demoKey", 0));

        }

        public static APIKeyVerifier Instance
        {
            get
            {
                if (instance == null)
                {
                    lock (locker)
                    {
                        if (instance == null)
                        {
                            instance = new APIKeyVerifier();
                        }
                    }
                }

                return instance;
            }
        }

        public bool CreateKey(string emailAddress, out string errorMessage)
        {
            // If the email address is not present in the dictionary we add it
            foreach (string key in usersMappedAPIKeys.Keys)
            {
                if (usersMappedAPIKeys[key].Email.Equals(emailAddress))
                {
                    errorMessage = String.Format(ErrorMessages.EMAIL_IN_USE, emailAddress);
                    return false;
                }
            }

            User newUser = new User(emailAddress);
            usersMappedAPIKeys.Add(newUser.Key, newUser);
            sendEMail(newUser);

            saveToXDocument(newUser, Path.Combine(System.Web.HttpContext.Current.Server.MapPath(@"~/App_Data"), "keys.xml"));
            errorMessage = String.Format(ErrorMessages.API_KEY_CREATED, emailAddress);
            return true;
        }

        public bool VerifyAPIKeyValidity(string APIKey, out string errorMessage)
        {
            if (APIKey == null)
            {
                errorMessage = ErrorMessages.API_KEY_NOT_VALID;
                return false;
            }

            if (usersMappedAPIKeys.ContainsKey(APIKey))
            {
                if (usersMappedAPIKeys[APIKey].Hits < MAXIMUM_NUMBER_HITS_PER_KEY)
                {
                    usersMappedAPIKeys[APIKey].Hits++;
                    errorMessage = ErrorMessages.API_KEY_VALID;
                    return true;
                }
                else
                {
                    errorMessage = String.Format(ErrorMessages.MAXIMUM_HITS_PER_KEY_REACHED, MAXIMUM_NUMBER_HITS_PER_KEY);
                    return false;
                }
            }

            errorMessage = ErrorMessages.API_KEY_NOT_VALID;
            return false;
        }

        public void LoadXDocument(string path)
        {
            XDocument doc = XDocument.Load(path);

            List<XElement> usersElement = doc.Descendants("user").ToList<XElement>();
            foreach (XElement user in usersElement)
            {
                string email = user.Element("email").Value;
                string key = user.Element("key").Value;
                int hits = Convert.ToInt32(user.Element("hits").Value);

                if (!usersMappedAPIKeys.ContainsKey(key))
                {
                    usersMappedAPIKeys.Add(key, new User(email, key, hits));
                }
            }
        }

        private void saveToXDocument(User newUser, string path)
        {
            XDocument doc = XDocument.Load(path);
            doc.Root.Add(new XElement("user",
                new XElement("email", newUser.Email),
                new XElement("key", newUser.Key),
                new XElement("hits", newUser.Hits)));
            doc.Save(Path.Combine(System.Web.HttpContext.Current.Server.MapPath(@"~/App_Data"), "keys.xml"));
        }

        private void sendEMail(User user)
        {
            MailMessage message = new MailMessage();
            message.To.Add(user.Email);
            message.Subject = "SOMA API Key";
            message.From = new System.Net.Mail.MailAddress(ConfigurationManager.AppSettings["SomaEmail"]);
            message.Body = "Your key is " + user.Key;

            NetworkCredential credentials = new NetworkCredential(ConfigurationManager.AppSettings["SomaEmail"], ConfigurationManager.AppSettings["SomaPasswd"]);

            SmtpClient smtp = new SmtpClient();
            smtp.Host = "smtp.gmail.com";
            smtp.Port = 587;
            smtp.Credentials = credentials;
            smtp.EnableSsl = true;
            smtp.Send(message);
        }

    }
}